import { NodeTypes } from "./types/NodeTypes";
import { Util } from "./Util";
import { BaseLayer } from "./BaseLayer";
import { Drag } from "./Drag";
import { Transform } from "./Transform";

const SET = "set";
let idCounter = 1;

export abstract class Node<Types extends NodeTypes = NodeTypes> {
  _id = idCounter++;
  parent: any;
  attrs: any = {};

  // 事件监听
  eventListeners: {
    [index: string]: Array<{ name: string; handler: Function }>;
  } = {};

  constructor(types: Types) {
    // 设置初始参数
    this.setAttrs(types);
    // 是否开启拖拽事件监听
    if (this.attrs.draggable) {
      this._listenDrag();
    }
  }

  abstract drawScene(): void;
  abstract drawHit(): void;

  _fireAndBubble(eventType, evt) {
    this._fire(eventType, evt);
    if (!evt.cancelBubble && this.parent) {
    }
  }

  _fire(eventType, evt) {
    const events = this.eventListeners[eventType];
    if (events) {
      evt = evt || {};
      evt.currentTarget = this;
      evt.type = eventType;

      for (let i = 0; i < events.length; i++) {
        events[i].handler.call(this, evt);
      }
    }
  }

  _setAttr(key, val) {
    const oldVal = this.attrs[key];
    if (oldVal === val && !Util.isObject(val)) {
      return;
    }
    if (!val) {
      delete this.attrs[key];
    } else {
      this.attrs[key] = val;
    }
  }

  _listenDrag() {
    this._dragCleanUp();
    this.on("mousedown", (evt) => {
      this._createDragElement(evt);
    });
  }

  _dragCleanUp() {
    this.off("mousedown");
  }

  // 创建拖拽元素
  _createDragElement(evt) {
    const pos = this.getLayer().getParent().getPointerPosition();
    Drag._dragElements.set(this._id, {
      node: this,
      startPointerPos: pos,
      offset: {
        left: pos.left - this.getLeft(),
        top: pos.top - this.getTop(),
      },
      dragStatus: "ready",
    });
  }

  // 设置拖拽位置
  _setDragPosition(evt, elem) {
    const pos = this.getLayer().getParent().getPointerPosition();

    this.setLeft(pos.left - elem.offset.left);
    this.setTop(pos.top - elem.offset.top);
    this.getLayer().batchDraw();
  }

  _getAbsoluteTransform() {
    let at = new Transform();
    this._eachAncestorReverse((node: Node<Types>) => {
      at.multiply(node.getTransform());
    });
    return at;
  }

  _eachAncestorReverse(func) {
    let family = [],
      parent = this.getParent(),
      len,
      n;

    family.unshift(this);

    while (parent) {
      family.unshift(parent);
      parent = parent.parent;
    }

    len = family.length;
    for (n = 0; n < len; n++) {
      func(family[n]);
    }
  }

  // 绘制
  draw() {
    this.drawScene();
    this.drawHit();
  }

  // 事件监听
  on(eventType, handler) {
    let events = this.eventListeners[eventType];
    if (!events) {
      events = this.eventListeners[eventType] = [];
    }
    events.push({
      name: "",
      handler,
    });
  }

  // 取消事件监听
  off(eventType) {
    delete this.eventListeners[eventType];
  }

  // 获取图层
  getLayer(): BaseLayer {
    let parent = this.getParent();
    return parent ? parent.getLayer() : null;
  }
  // 设置属性
  setAttrs(types: Types) {
    let key: any = "";
    let method: any = "";

    if (!types) {
      return this;
    }
    for (key in types) {
      method = SET + Util.capitalize(key);
      if (Util.isFunction(this[method])) {
        this[method](types[key]);
      } else {
        this._setAttr(key, types[key]);
      }
    }
    return this;
  }
  // 获取父级
  getParent() {
    return this.parent;
  }
  // 获取宽度
  getWidth() {
    return this.attrs.width;
  }
  // 获取高度
  getheight() {
    return this.attrs.height;
  }

  // 开始拖拽
  startDrag(evt?: any) {
    const elem = Drag._dragElements.get(this._id);
    elem.dragStatus = "dragging";
  }
  // 获取当前对象的top
  getTop() {
    return this.attrs.top;
  }
  // 设置当前对象的top
  setTop(val: number) {
    this.attrs.top = val;
  }
  // 获取当前对象的left
  getLeft() {
    return this.attrs.left;
  }
  // 设置当前对象的left
  setLeft(val: number) {
    this.attrs.left = val;
  }

  getTransform(): Transform {
    const m = new Transform();
    
    const left = this.getLeft();
    const top = this.getTop();

    const rotation = (this.getRotation() * Math.PI) / 180;
    const scaleX = this.getScaleX();
    const scaleY = this.getScaleY();

    const offsetX = this.getOffsetX();
    const offsetY = this.getOffsetY();

    if (left || top) {
      m.translate(left, top);
    }
    if (rotation !== 0) {
      m.rotate(rotation);
    }

    if (scaleX !== 1 || scaleY !== 1) {
      m.scale(scaleX, scaleY);
    }

    if (offsetX !== 0 || offsetY !== 0) {
      m.translate(offsetX, offsetY);
    }

    return m;
  }
  getAbsoluteTransform() {
    return this._getAbsoluteTransform();
  }

  getScaleX() {
    return this.attrs.scaleX || 1;
  }
  setScaleX(val: number) {
    this.attrs.scaleX = val;
  }
  getScaleY() {
    return this.attrs.scaleY || 1;
  }

  setScaleY(val: number) {
    this.attrs.scaleY = val;
  }

  getOffsetX() {
    return this.attrs.offsetX || 0;
  }

  setOffsetX(val: number) {
    this.attrs.offsetX = val;
  }

  getOffsetY() {
    return this.attrs.offsetY || 0;
  }

  setOffsetY(val: number) {
    this.attrs.offsetY = val;
  }

  getRotation() {
    return this.attrs.rotation || 0;
  }

  setRotation(val: number) {
    this.attrs.rotation = val;
  }
}
